#ifndef FFTENGINE_H
#define FFTENGINE_H

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "Complex.h"
#include "CommLayer.h"

#define PI (asin(1.0)*2.0)

class FFTEngine {

public:

	int rank;
	int procs;
	int numPoints;
	int numLocalPoints;
	int numNodes;
	CommLayer *comm;

	Complex *localData;
	Complex *localBuff;


	/** Constructors **/
	FFTEngine(int iNumPoints, CommLayer *iComm); 
	~FFTEngine();


	/** FFT Driver functions **/
	virtual void distributeInitData(Complex *arr); //called by rank=0
	virtual void aquireInitData(); //called by rank>0
	
	virtual void acquireResultData(Complex *arr); //called by rank=0
	virtual void distributeResultData(); //called by rank>0

	virtual void executeFFT(bool commOnly); //computes the FFT in localData
	virtual void executeFFT() { executeFFT(false); }
	virtual void runFFT(Complex *arr); //distributes data AND computes FFT




	/** utility functions **/
	int bitRev(int oldVal, int maxVal);
	static Complex omega(int base) { return Complex(cos(2.0 * PI / base),sin(2.0 * PI / base)); }
	static Complex omega(int base,int pow) { return Complex(cos(2.0*PI*pow/base),sin(2.0*PI*pow/base)); }


	/** local functions **/
	void local_computeFFT(Complex *arr);
	void local_bitReverse(Complex *arr);


	/** debugging functions **/
	void printLocalData();	


	/**Unit Tests **/
	virtual void unitTests();
	virtual void testDistributeData();
	virtual void testFFT();

protected:
	
	virtual void distributeData(Complex *arr,bool isScatter, bool isCyclic, bool isBitReversed);


	virtual bool isLocal(int globalId) { return globalNode(globalId) == rank; }
	bool blocked_isLocal(int globalId) { return blocked_globalNode(globalId) == rank; }
	bool cyclic_isLocal(int globalId) { return cyclic_globalNode(globalId) == rank; }
	
	virtual int global2localId(int globalId) { return cyclic_global2localId(globalId); }	
	int blocked_global2localId(int globalId) { return globalId - (rank*numLocalPoints); }
	int cyclic_global2localId(int globalId) { return (globalId-rank)/numNodes; }
	
	virtual int globalNode(int globalId) { return cyclic_globalNode(globalId); }	
	int blocked_globalNode(int globalId) { return globalId/numLocalPoints; }	
	int cyclic_globalNode(int globalId) { return globalId%numNodes; }	


	int pow(int v, int p);
	int log2(int v);
	void revCopy(Complex *arr, int n);

};

#endif

